package org.desktop.web.client.gui.models;

import java.util.ArrayList;
import java.util.List;

import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.rpc.IsSerializable;
import com.google.gwt.user.client.ui.IsWidget;
import com.google.gwt.user.client.ui.Widget;
import com.sencha.gxt.data.client.loader.RpcProxy;
import com.sencha.gxt.data.shared.ListStore;
import com.sencha.gxt.data.shared.ModelKeyProvider;
import com.sencha.gxt.data.shared.PropertyAccess;
import com.sencha.gxt.data.shared.loader.FilterPagingLoadConfig;
import com.sencha.gxt.data.shared.loader.FilterPagingLoadConfigBean;
import com.sencha.gxt.data.shared.loader.LoadResultListStoreBinding;
import com.sencha.gxt.data.shared.loader.PagingLoadConfig;
import com.sencha.gxt.data.shared.loader.PagingLoadResult;
import com.sencha.gxt.data.shared.loader.PagingLoader;
import com.sencha.gxt.widget.core.client.container.VerticalLayoutContainer;
import com.sencha.gxt.widget.core.client.container.VerticalLayoutContainer.VerticalLayoutData;
import com.sencha.gxt.widget.core.client.event.CellClickEvent;
import com.sencha.gxt.widget.core.client.event.CellClickEvent.CellClickHandler;
import com.sencha.gxt.widget.core.client.event.RefreshEvent;
import com.sencha.gxt.widget.core.client.event.RefreshEvent.RefreshHandler;
import com.sencha.gxt.widget.core.client.event.RowClickEvent;
import com.sencha.gxt.widget.core.client.event.RowClickEvent.RowClickHandler;
import com.sencha.gxt.widget.core.client.grid.ColumnConfig;
import com.sencha.gxt.widget.core.client.grid.ColumnModel;
import com.sencha.gxt.widget.core.client.grid.Grid;
import com.sencha.gxt.widget.core.client.grid.editing.GridEditing;
import com.sencha.gxt.widget.core.client.grid.editing.GridInlineEditing;
import com.sencha.gxt.widget.core.client.grid.filters.Filter;
import com.sencha.gxt.widget.core.client.grid.filters.GridFilters;
import com.sencha.gxt.widget.core.client.info.Info;
import com.sencha.gxt.widget.core.client.toolbar.PagingToolBar;
import com.sencha.gxt.widget.core.client.toolbar.ToolBar;

/**
 * @param <B>
 *            rappresenta il bean con i dati da visualizzare in griglia
 * @param <C>
 *            FilterPagingLoadConfig, PagingLoadConfig
 * @param <R>
 *            PagingLoadResult o ListLoadResult
 */
public abstract class APagingGrid<B extends IsSerializable, C extends PagingLoadConfig, PA extends PropertyAccess<B>>
		implements IsWidget {

	private PagingToolBar pagingToolBar;

	private Grid<B> grid;

	private VerticalLayoutContainer gridContainer;

	private ToolBar topToolBar;

	private ToolBar bottomToolBar;

	/**
	 * Griglia figlia, i cui dati vanno caricati quando viene selezionata una
	 * riga di questa griglia
	 */
	private APagingGrid<?, ?, ?> childGrid;

	private ListStore<B> store;

	private boolean remoteSort;

	private PagingLoader<C, PagingLoadResult<B>> loader;

	private APagingGrid<?, ?, ?> parent;

	private GridEditing<B> gridEditing;

	private boolean editable = false;

	private RpcProxy<C, PagingLoadResult<B>> rpcProxy;

	public APagingGrid(int pageSize, APagingGrid<?, ?, ?> childGrid,
			boolean remoteSort, APagingGrid<?, ?, ?> parent) {
		this.pagingToolBar = new PagingToolBar(pageSize);
		this.childGrid = childGrid;
		this.remoteSort = remoteSort;
		this.parent = parent;
		if (this.childGrid != null) {
			this.childGrid.setParent(this);
		}
	}

	public APagingGrid(int pageSize, APagingGrid<?, ?, ?> childGrid,
			boolean remoteSort) {
		this(pageSize, childGrid, remoteSort, null);
	}

	public APagingGrid(int pageSize, boolean remoteSort,
			APagingGrid<?, ?, ?> parent) {
		this(pageSize, null, remoteSort, parent);
	}

	public APagingGrid(int pageSize, APagingGrid<?, ?, ?> parent) {
		this(pageSize, null, false, parent);
	}

	public APagingGrid(int pageSize) {
		this(pageSize, null, false, null);
	}

	public abstract PA getPropertyAccess();

	// public abstract PagingLoader<C, PagingLoadResult<B>> getLoader();

	/**
	 * Inizializza il proxy e lo valorizza. L'implementazione è lasciata alla
	 * classe delle griglia
	 */
	public abstract void initProxy();

	public abstract List<ColumnConfig<B, ?>> getColumnsConfig();

	/**
	 * deve restituire il valore del metodo key() della PropertyAccess
	 * corrispondente!
	 * 
	 * @return
	 */
	public abstract ModelKeyProvider<? super B> key();

	/**
	 * Metodo non implementato, va implementato al bisogno nelle classi figlie
	 */
	public ToolBar createTopToolBar() {
		return null;
	}

	/**
	 * Metodo non implementato, va implementato al bisogno nelle classi figlie
	 */
	public ToolBar createBottomToolBar() {
		return null;
	}

	public Widget asWidget() {
		gridContainer = new VerticalLayoutContainer();
		if (getRpcProxy() == null) {
			initProxy();
		}
		this.loader = new PagingLoader<C, PagingLoadResult<B>>(getRpcProxy()) {

			@Override
			protected C newLoadConfig() {
				// TODO Auto-generated method stub
				FilterPagingLoadConfigBean loadConfigBean = new FilterPagingLoadConfigBean();
				// loadConfigBean.setLimit(2);
				// loadConfigBean.setOffset(1);
				return (C) loadConfigBean;
			}

		};
		ListStore<B> store = getStore();
		addStoreHandlers();
		loader.setRemoteSort(this.remoteSort);
		loader.addLoadHandler(new LoadResultListStoreBinding<C, B, PagingLoadResult<B>>(
				store));

		if (getPagingToolBar() != null) {
			getPagingToolBar().bind(loader);

		}

		ColumnModel<B> cm = new ColumnModel<B>(getColumnsConfig());

		List<Filter<B, ?>> filters = filters();

		grid = new Grid<B>(store, cm);
		if (getChildGrid() != null) {
			final APagingGrid<?, ?, ?> ch = getChildGrid();
			grid.addRowClickHandler(new RowClickHandler() {

				@Override
				public void onRowClick(RowClickEvent event) {

					ch.loadByRowSelected(getStore().get(event.getRowIndex()));
				}
			});
			grid.addRefreshHandler(new RefreshHandler() {

				@Override
				public void onRefresh(RefreshEvent event) {
					ch.grid.getStore().clear();
				}
			});
		}
		grid.getView().setForceFit(true);

		grid.setLoader(loader);
		grid.setBorders(true);

		if (isEditable()) {
			this.gridEditing = new GridInlineEditing<B>(this.grid);
			onCompleteEditing(this.gridEditing);
			defineColumnsEditing();
		}

		// se è una griglia senza genitori allora carico subito i dati
		if (this.parent == null) {
			load();
		}
		if (filters != null && filters.size() > 0) {
			GridFilters<B> gridFilters = new GridFilters<B>(
					(PagingLoader<FilterPagingLoadConfig, PagingLoadResult<B>>) this.loader);
			gridFilters.initPlugin(grid);
			for (Filter<B, ?> filter : filters) {
				gridFilters.addFilter(filter);
			}
		}

		addCellClickHandler();
		// le toolbar vanno create al fondo di tutto il processo per poter
		// interagire con gli altri componenti della griglia
		this.topToolBar = createTopToolBar();
		this.bottomToolBar = createBottomToolBar();

		if (topToolBar != null) {
			gridContainer.add(topToolBar);// ,new VerticalLayoutData(1, -1));
		}

		gridContainer.add(grid, new VerticalLayoutData(1, 1));
		if (bottomToolBar != null) {
			gridContainer.add(bottomToolBar, new VerticalLayoutData(1, -1));
		}
		// TODO Auto-generated method stub
		return gridContainer;
	}

	/**
	 * Metodo utile per aggiungere allo store tutti gli handler necessari!
	 * L'implementazione viene lasciata alla classe figlia
	 */
	protected void addStoreHandlers() {

	}

	/**
	 * aggiunge le colonne editabili della griglia. Va implementato dalle classi
	 * figlie
	 */
	protected void defineColumnsEditing() {
		// TODO Auto-generated method stub

	}

	private void addCellClickHandler() {
		grid.addCellClickHandler(getCellClickHandler());
	}

	public CellClickHandler getCellClickHandler() {
		return new CellClickHandler() {

			@Override
			public void onCellClick(CellClickEvent event) {
				// TODO Auto-generated method stub

			}
		};
	}

	/**
	 * 
	 */
	protected void onCompleteEditing(GridEditing<B> gridEditing) {
	}

	/**
	 * Metodo che carica i dati della griglia sulla base di una selezione fatta
	 * sulla griglia padre
	 * 
	 * @param parentSelected
	 */
	public void loadByRowSelected(Object parentSelected) {
		// TODO Auto-generated method stub

	}

	public void load() {
		grid.setLoadMask(true);
		this.loader.load();

	}

	/**
	 * Metodo per la gestione dei filtri sulla griglia. Se si vuole implementare
	 * il filtro sulle colonne occorre implementare questo metodo. <Br>
	 * Come valore di default viene restituita una lista vuota
	 * 
	 * @return
	 */
	public List<Filter<B, ?>> filters() {
		return new ArrayList<Filter<B, ?>>();
	}

	public ListStore<B> getStore() {
		if (store == null) {
			store = new ListStore<B>(key());
		}
		return store;
	}

	public PagingToolBar getPagingToolBar() {
		return pagingToolBar;
	}

	public void setPagingToolBar(PagingToolBar pagingToolBar) {
		this.pagingToolBar = pagingToolBar;
	}

	public Grid<B> getGrid() {
		return grid;
	}

	public APagingGrid<?, ?, ?> getChildGrid() {
		return childGrid;
	}

	public void setChildGrid(APagingGrid<?, ?, ?> childGrid) {
		this.childGrid = childGrid;
	}

	public APagingGrid<?, ?, ?> getParent() {
		return parent;
	}

	public void setParent(APagingGrid<?, ?, ?> parent) {
		this.parent = parent;
	}

	public final RpcProxy<C, PagingLoadResult<B>> getRpcProxy() {
		return rpcProxy;
	}

	protected final void setRpcProxy(RpcProxy<C, PagingLoadResult<B>> rpcProxy) {
		this.rpcProxy = rpcProxy;
	}

	public ToolBar getTopToolBar() {
		return topToolBar;
	}

	public ToolBar getBottomToolBar() {
		return bottomToolBar;
	}

	public boolean isEditable() {
		return editable;
	}

	public void setEditable(boolean editable) {
		this.editable = editable;
	}

	public GridEditing<B> getGridEditing() {
		return gridEditing;
	}

}
